<template>
  <div>
    <canvas ref="bjsCanvas" style="width: 100%; height: 100vh"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import * as BABYLON from '@babylonjs/core/Legacy/legacy'
import '@babylonjs/loaders'

const bjsCanvas = ref(null)
const engine = ref(null)
const scene = ref(null)
const lights = ref([])
const materials = ref({})

const createScene = () => {
  const newScene = new BABYLON.Scene(engine.value)

  // 相机
  const camera = new BABYLON.ArcRotateCamera(
      "camera",
      -Math.PI / 2,
      Math.PI / 2.5,
      15,
      BABYLON.Vector3.Zero(),
      newScene
  )
  camera.attachControl(bjsCanvas.value, true)

  // 环境光
  new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), newScene)

  // 创建灯光和物体
  createNeonLights(newScene)
  createMaterials(newScene)
  createObjects(newScene)

  // 添加动画
  newScene.registerBeforeRender(() => {
    const time = Date.now() * 0.001
    animateLights(time)
    animateMaterials(time)
  })

  return newScene
}

const createNeonLights = (scene) => {
  const colors = [
    BABYLON.Color3.Red(),
    BABYLON.Color3.Green(),
    BABYLON.Color3.Blue(),
    BABYLON.Color3.Magenta()
  ]

  lights.value = colors.map((color, i) => {
    const light = new BABYLON.PointLight(
        `neonLight${i}`,
        new BABYLON.Vector3(
            -10+i*10,
            2,
            0
        ),
        scene
    )
    light.diffuse = color
    light.specular = color
    light.intensity = 1
    return light
  })
}

const createMaterials = (scene) => {
  materials.value = {
    metalMat: createMetalMaterial(scene),
    neonMat: createNeonMaterial(scene),
    glassMat: createGlassMaterial(scene)
  }
}

const createMetalMaterial = (scene) => {
  const mat = new BABYLON.PBRMaterial("metal", scene)
  mat.metallic = 1
  mat.roughness = 0.2
  mat.environmentIntensity = 0.8
  return mat
}

const createNeonMaterial = (scene) => {
  const mat = new BABYLON.PBRMaterial("neon", scene)
  mat.emissiveColor = new BABYLON.Color3(0.5, 0.5, 0.5)
  mat.emissiveIntensity = 0.8
  mat.roughness = 0.8
  return mat
}

const createGlassMaterial = (scene) => {
  const mat = new BABYLON.PBRMaterial("glass", scene)
  mat.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND
  mat.alpha = 0.5
  mat.metallic = 0
  mat.roughness = 0
  mat.indexOfRefraction = 0.52
  return mat
}

const createObjects = (scene) => {
  // 金属球
  const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2 }, scene)
  sphere.position.y = 2
  sphere.material = materials.value.metalMat

  // 发光立方体
  const box = BABYLON.MeshBuilder.CreateBox("box", { size: 2 }, scene)
  box.position.y = -2
  box.material = materials.value.neonMat

  // 玻璃环面
  const torus = BABYLON.MeshBuilder.CreateTorus(
      "torus",
      { diameter: 3, thickness: 0.5 },
      scene
  )
  torus.material = materials.value.glassMat
}

const animateLights = (time) => {
  lights.value.forEach((light, i) => {
    const hue = (time * 0.2 + i * 0.25) % 1
    const color = BABYLON.Color3.FromHSV(hue, 0.9, 1)
    light.diffuse = color
    light.specular = color
    light.intensity = Math.sin(time * 2 + i) * 0.5 + 1.5
  })
}

const animateMaterials = (time) => {
  materials.value.neonMat.emissiveIntensity = Math.sin(time * 3) * 0.3 + 0.7
}

const handleResize = () => {
  engine.value.resize()
}

onMounted(() => {
  engine.value = new BABYLON.Engine(bjsCanvas.value, true)
  scene.value = createScene()

  engine.value.runRenderLoop(() => {
    scene.value.render()
  })

  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  scene.value.dispose()
  engine.value.dispose()
})
</script>